package com.iteaj.iot.serial;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.iteaj.iot.client.ClientComponent;
import com.iteaj.iot.client.IotClient;
import com.iteaj.iot.resolver.FunctionResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.function.Consumer;

public class SerialClient implements IotClient {

    private SerialPort serialPort;
    private SerialPortDataListener listener;
    private SerialComponent serialComponent;
    private SerialConnectProperties connectProperties;
    private Logger logger = LoggerFactory.getLogger(getClass());

    public SerialClient(SerialComponent serialComponent, SerialConnectProperties connectProperties) {
        this.serialComponent = serialComponent;
        this.connectProperties = connectProperties;
    }

    public boolean isOpen() {
        return this.serialPort.isOpen();
    }

    @Override
    public int getPort() {
        throw new UnsupportedOperationException("不支持的操作");
    }

    @Override
    public void init(Object arg) {
        this.serialPort = SerialPort.getCommPort(this.connectProperties.connectKey());
        this.serialPort.setComPortParameters(this.connectProperties.getBaudRate()
                , this.connectProperties.getDataBits(), this.connectProperties.getStopBits()
                , this.connectProperties.getParity(), this.connectProperties.isUseRS485Mode());

        if(arg instanceof SerialPortDataListener) {
            this.addDataListener((SerialPortDataListener) arg);
        }

        int timeoutMode = SerialPort.TIMEOUT_READ_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING;
        this.serialPort.setComPortTimeouts(timeoutMode, (int) this.connectProperties.getReaderIdleTime() * 1000
                , (int) this.connectProperties.getWriterIdleTime() * 1000);
    }

    /**
     * 当前串口可读的字节数
     * @return 当前可读取的字节数, 如果端口未打开则为-1
     */
    public int bytesAvailable() {
        return this.serialPort.bytesAvailable();
    }

    /**
     * @return 返回仍在等待写入设备输出的字节数
     */
    public int bytesAwaitingWrite() {
        return this.serialPort.bytesAwaitingWrite();
    }

    /**
     * 从串口读取数据
     * @see SerialConnectProperties#getReaderIdleTime() 读超时(秒)
     * @param msg
     * @return
     */
    public int read(byte[] msg) {
        return this.serialPort.readBytes(msg, msg.length);
    }

    /**
     * 从串口读取数据
     * @see SerialConnectProperties#getReaderIdleTime() 读超时(秒)
     * @param msg
     * @return
     */
    public int read(byte[] msg, int offset) {
        return this.serialPort.readBytes(msg, msg.length, offset);
    }

    /**
     * 写数据到串口
     * @see SerialConnectProperties#getWriterIdleTime() 写超时(秒)
     * @param msg
     * @return
     */
    public int write(byte[] msg) {
        return this.serialPort.writeBytes(msg, msg.length);
    }

    /**
     * 写数据到串口
     * @see SerialConnectProperties#getWriterIdleTime() 写超时(秒)
     * @param msg
     * @return
     */
    public int write(byte[] msg, int offset) {
        return this.serialPort.writeBytes(msg, msg.length - offset, offset);
    }

    @Override
    public Boolean connect() {
        boolean openPort = this.serialPort.openPort((int) getConfig().getConnectTimeout()
                , this.connectProperties.getSendDeviceQueueSize()
                , this.connectProperties.getReceiveDeviceQueueSize());
        if(openPort) {
            this.getClientComponent().addClient(this.connectProperties, this);
        }

        String style = "同步";
        if(this.listener instanceof SerialPortDelimiterListener) {
            style = "异步(分隔符)";
        } else if(this.listener instanceof SerialPortPacketProtocolListener) {
            style = "异步(固定长度)";
        }

        if(openPort) {
            if(logger.isInfoEnabled()) {
                logger.info("串口客户端({}) 打开成功 - 方式：{}", this.connectProperties, style);
            }
        } else {
            logger.error("串口客户端({}) 打开失败 - 方式：{}", this.connectProperties, style);
        }

        return openPort;
    }

    @Override
    public Boolean disconnect() {
        boolean status = this.serialPort.isOpen() ? this.serialPort.closePort() : true;
        if(status) {
            logger.info("串口客户端({}) 关闭成功", this.connectProperties);
        } else {
            logger.error("串口客户端({}) 关闭失败", this.connectProperties);
        }

        return status;
    }

    /**
     * 增加数据监听
     * @param dataListener
     * @return
     */
    public synchronized boolean addDataListener(SerialPortDataListener dataListener) {
        this.listener = dataListener;
        return this.getSerialPort().addDataListener(dataListener);
    }

    /**
     * 移除监听
     * @return
     */
    public synchronized void removeDataListener() {
        if(this.getListener() != null) {
            this.listener = null;
            this.getSerialPort().removeDataListener();
        }
    }

    /**
     * 关闭串口
     * @return
     */
    @Override
    public Boolean close() {
        if(this.disconnect()) {
            this.getClientComponent().removeClient(this.getConfig());
            return true;
        }

        return false;
    }

    @Override
    public ClientComponent getClientComponent() {
        return this.serialComponent;
    }

    public SerialPort getSerialPort() {
        return serialPort;
    }

    @Override
    public SerialConnectProperties getConfig() {
        return connectProperties;
    }

    public SerialPortDataListener getListener() {
        return listener;
    }
}
